 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Benjamin Muskalla - Bug 29633 [EditorMgmt] "Open" menu should
  * have Open With-->Other
  *******************************************************************************/
 package org.eclipse.ui.internal.dialogs;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.content.IContentType;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.preference.PreferencePage;
 import org.eclipse.jface.window.Window;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableItem;
 import org.eclipse.ui.IEditorDescriptor;
 import org.eclipse.ui.IFileEditorMapping;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPreferencePage;
 import org.eclipse.ui.dialogs.EditorSelectionDialog;
 import org.eclipse.ui.dialogs.PreferenceLinkArea;
 import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
 import org.eclipse.ui.internal.WorkbenchMessages;
 import org.eclipse.ui.internal.WorkbenchPlugin;
 import org.eclipse.ui.internal.registry.EditorDescriptor;
 import org.eclipse.ui.internal.registry.EditorRegistry;
 import org.eclipse.ui.internal.registry.FileEditorMapping;
 import org.eclipse.ui.internal.util.PrefUtil;
 import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;

 /**
  * The file editors page presents the collection of file names and extensions
  * for which the user has registered editors. It also lets the user add new
  * internal or external (program) editors for a given file name and extension.
  *
  * The user can add an editor for either a specific file name and extension
  * (e.g. report.doc), or for all file names of a given extension (e.g. *.doc)
  *
  * The set of registered editors is tracked by the EditorRegistery
  * available from the workbench plugin.
  */
 public class FileEditorsPreferencePage extends PreferencePage implements
         IWorkbenchPreferencePage, Listener {
     
     private static final String DATA_EDITOR = "editor"; //$NON-NLS-1$

     private static final String DATA_FROM_CONTENT_TYPE = "type"; //$NON-NLS-1$

     protected Table resourceTypeTable;

     protected Button addResourceTypeButton;

     protected Button removeResourceTypeButton;

     protected Table editorTable;

     protected Button addEditorButton;

     protected Button removeEditorButton;

     protected Button defaultEditorButton;

     protected Label editorLabel;

     protected IWorkbench workbench;

     protected List imagesToDispose;

     protected Map editorsToImages;

     /**
      * Add a new resource type to the collection shown in the top of the page.
      * This is typically called after the extension dialog is shown to the user.
      *
      * @param newName the new name
      * @param newExtension the new extension
      */
     public void addResourceType(String newName, String newExtension) {
         // Either a file name or extension must be provided
 Assert.isTrue((newName != null && newName.length() != 0)
                 || (newExtension != null && newExtension.length() != 0));

         // Wild card only valid by itself (i.e. rep* is not valid)
 // And must have an extension
 int index = newName.indexOf('*');
         if (index > -1) {
             Assert.isTrue(index == 0 && newName.length() == 1);
             Assert.isTrue(newExtension != null && newExtension.length() != 0);
         }

         // Find the index at which to insert the new entry.
 String newFilename = (newName + (newExtension == null
                 || newExtension.length() == 0 ? "" : "." + newExtension)).toUpperCase();//$NON-NLS-1$ //$NON-NLS-2$
 IFileEditorMapping resourceType;
         TableItem[] items = resourceTypeTable.getItems();
         boolean found = false;
         int i = 0;

         while (i < items.length && !found) {
             resourceType = (IFileEditorMapping) items[i].getData();
             int result = newFilename.compareToIgnoreCase(resourceType
                     .getLabel());
             if (result == 0) {
                 // Same resource type not allowed!
 MessageDialog
                         .openInformation(
                                 getControl().getShell(),
                                 WorkbenchMessages.FileEditorPreference_existsTitle,
                                 WorkbenchMessages.FileEditorPreference_existsMessage);
                 return;
             }

             if (result < 0) {
                 found = true;
             } else {
                 i++;
             }
         }

         // Create the new type and insert it
 resourceType = new FileEditorMapping(newName, newExtension);
         TableItem item = newResourceTableItem(resourceType, i, true);
         resourceTypeTable.setFocus();
         resourceTypeTable.showItem(item);
         fillEditorTable();
     }

     /**
      * Creates the page's UI content.
      */
     protected Control createContents(Composite parent) {
         imagesToDispose = new ArrayList ();
         editorsToImages = new HashMap (50);

         // define container & its gridding
 Composite pageComponent = new Composite(parent, SWT.NULL);
         GridLayout layout = new GridLayout();
         layout.numColumns = 2;
         layout.marginWidth = 0;
         layout.marginHeight = 0;
         pageComponent.setLayout(layout);
         GridData data = new GridData();
         data.verticalAlignment = GridData.FILL;
         data.horizontalAlignment = GridData.FILL;
         pageComponent.setLayoutData(data);

         //layout the contents

         PreferenceLinkArea contentTypeArea = new PreferenceLinkArea(pageComponent, SWT.NONE,
                 "org.eclipse.ui.preferencePages.ContentTypes", WorkbenchMessages.FileEditorPreference_contentTypesRelatedLink,//$NON-NLS-1$
 (IWorkbenchPreferenceContainer) getContainer(),null);
         
         data = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
         contentTypeArea.getControl().setLayoutData(data);

         //layout the top table & its buttons
 Label label = new Label(pageComponent, SWT.LEFT);
         label.setText(WorkbenchMessages.FileEditorPreference_fileTypes);
         data = new GridData();
         data.horizontalAlignment = GridData.FILL;
         data.horizontalSpan = 2;
         label.setLayoutData(data);

         resourceTypeTable = new Table(pageComponent, SWT.SINGLE | SWT.BORDER
                 | SWT.FULL_SELECTION);
         resourceTypeTable.addListener(SWT.Selection, this);
         resourceTypeTable.addListener(SWT.DefaultSelection, this);
         data = new GridData(GridData.FILL_HORIZONTAL);

         int availableRows = DialogUtil.availableRows(pageComponent);

         data.heightHint = resourceTypeTable.getItemHeight()
                 * (availableRows / 8);
         resourceTypeTable.setLayoutData(data);

         Composite groupComponent = new Composite(pageComponent, SWT.NULL);
         GridLayout groupLayout = new GridLayout();
         groupLayout.marginWidth = 0;
         groupLayout.marginHeight = 0;
         groupComponent.setLayout(groupLayout);
         data = new GridData();
         data.verticalAlignment = GridData.FILL;
         data.horizontalAlignment = GridData.FILL;
         groupComponent.setLayoutData(data);

         addResourceTypeButton = new Button(groupComponent, SWT.PUSH);
         addResourceTypeButton.setText(WorkbenchMessages.FileEditorPreference_add);
         addResourceTypeButton.addListener(SWT.Selection, this);
         addResourceTypeButton.setLayoutData(data);
         setButtonLayoutData(addResourceTypeButton);

         removeResourceTypeButton = new Button(groupComponent, SWT.PUSH);
         removeResourceTypeButton.setText(WorkbenchMessages.FileEditorPreference_remove);
         removeResourceTypeButton.addListener(SWT.Selection, this);
         setButtonLayoutData(removeResourceTypeButton);

         //Spacer
 label = new Label(pageComponent, SWT.LEFT);
         data = new GridData();
         data.horizontalAlignment = GridData.FILL;
         data.horizontalSpan = 2;
         label.setLayoutData(data);

         // layout the bottom table & its buttons
 editorLabel = new Label(pageComponent, SWT.LEFT);
         editorLabel.setText(WorkbenchMessages.FileEditorPreference_associatedEditors);
         data = new GridData();
         data.horizontalAlignment = GridData.FILL;
         data.horizontalSpan = 2;
         editorLabel.setLayoutData(data);

         editorTable = new Table(pageComponent, SWT.SINGLE | SWT.BORDER);
         editorTable.addListener(SWT.Selection, this);
         editorTable.addListener(SWT.DefaultSelection, this);
         data = new GridData(GridData.FILL_BOTH);
         data.heightHint = editorTable.getItemHeight() * 7;
         editorTable.setLayoutData(data);

         groupComponent = new Composite(pageComponent, SWT.NULL);
         groupLayout = new GridLayout();
         groupLayout.marginWidth = 0;
         groupLayout.marginHeight = 0;
         groupComponent.setLayout(groupLayout);
         data = new GridData();
         data.verticalAlignment = GridData.FILL;
         data.horizontalAlignment = GridData.FILL;
         groupComponent.setLayoutData(data);

         addEditorButton = new Button(groupComponent, SWT.PUSH);
         addEditorButton.setText(WorkbenchMessages.FileEditorPreference_addEditor);
         addEditorButton.addListener(SWT.Selection, this);
         addEditorButton.setLayoutData(data);
         setButtonLayoutData(addEditorButton);

         removeEditorButton = new Button(groupComponent, SWT.PUSH);
         removeEditorButton.setText(WorkbenchMessages.FileEditorPreference_removeEditor);
         removeEditorButton.addListener(SWT.Selection, this);
         setButtonLayoutData(removeEditorButton);

         defaultEditorButton = new Button(groupComponent, SWT.PUSH);
         defaultEditorButton.setText(WorkbenchMessages.FileEditorPreference_default);
         defaultEditorButton.addListener(SWT.Selection, this);
         setButtonLayoutData(defaultEditorButton);

         fillResourceTypeTable();
         if (resourceTypeTable.getItemCount() > 0) {
             resourceTypeTable.setSelection(0);
         }
         fillEditorTable();
         updateEnabledState();

         workbench.getHelpSystem().setHelp(parent,
                 IWorkbenchHelpContextIds.FILE_EDITORS_PREFERENCE_PAGE);
         applyDialogFont(pageComponent);

         return pageComponent;
     }

     /**
      * The preference page is going to be disposed. So deallocate all allocated
      * SWT resources that aren't disposed automatically by disposing the page
      * (i.e fonts, cursors, etc). Subclasses should reimplement this method to
      * release their own allocated SWT resources.
      */
     public void dispose() {
         super.dispose();
         if (imagesToDispose != null) {
             for (Iterator e = imagesToDispose.iterator(); e.hasNext();) {
                 ((Image) e.next()).dispose();
             }
             imagesToDispose = null;
         }
         if (editorsToImages != null) {
             for (Iterator e = editorsToImages.values().iterator(); e.hasNext();) {
                 ((Image) e.next()).dispose();
             }
             editorsToImages = null;
         }
     }

     /**
      * Hook method to get a page specific preference store. Reimplement this
      * method if a page don't want to use its parent's preference store.
      */
     protected IPreferenceStore doGetPreferenceStore() {
         return WorkbenchPlugin.getDefault().getPreferenceStore();
     }

     protected void fillEditorTable() {
         editorTable.removeAll();
         FileEditorMapping resourceType = getSelectedResourceType();
         if (resourceType != null) {
             IEditorDescriptor[] array = resourceType.getEditors();
             for (int i = 0; i < array.length; i++) {
                 IEditorDescriptor editor = array[i];
                 TableItem item = new TableItem(editorTable, SWT.NULL);
                 item.setData(DATA_EDITOR, editor);
                 // Check if it is the default editor
 String defaultString = null;
                 if (resourceType != null) {
                     if (resourceType.getDefaultEditor() == editor && resourceType.isDeclaredDefaultEditor(editor)) {
                         defaultString = WorkbenchMessages.FileEditorPreference_defaultLabel;
                     }
                 }

                 if (defaultString != null) {
                     item.setText(editor.getLabel() + " " + defaultString); //$NON-NLS-1$
 } else {
                     item.setText(editor.getLabel());
                 }
                 item.setImage(getImage(editor));
             }
             
             // now add any content type editors
 EditorRegistry registry = (EditorRegistry) WorkbenchPlugin
                     .getDefault().getEditorRegistry();
             IContentType[] contentTypes = Platform.getContentTypeManager()
                     .findContentTypesFor(resourceType.getLabel());
             for (int i = 0; i < contentTypes.length; i++) {
                 array = registry.getEditorsForContentType(contentTypes[i]);
                 for (int j = 0; j < array.length; j++) {
                     IEditorDescriptor editor = array[j];
                     // don't add duplicates
 TableItem[] items = editorTable.getItems();
                     TableItem foundItem = null;
                     for (int k = 0; k < items.length; k++) {
                         if (items[k].getData(DATA_EDITOR).equals(editor)) {
                             foundItem = items[k];
                             break;
                         }
                     }
                     if (foundItem == null) {
                         TableItem item = new TableItem(editorTable, SWT.NULL);
                         item.setData(DATA_EDITOR, editor);
                         item.setData(DATA_FROM_CONTENT_TYPE, contentTypes[i]);
                         setLockedItemText(item, editor.getLabel());
                         item.setImage(getImage(editor));
                     } else { // update the item to reflect its origin
 foundItem.setData(DATA_FROM_CONTENT_TYPE, contentTypes[i]);
                         setLockedItemText(foundItem, foundItem.getText());
                     }
                 }
             }
             
         }
     }

     /**
      * Set the locked message on the item. Assumes the item has an instance of
      * IContentType in the data map.
      *
      * @param item the item to set
      * @param baseLabel the base label
      */
     private void setLockedItemText(TableItem item, String baseLabel) {
         item.setText(NLS
                 .bind(WorkbenchMessages.FileEditorPreference_isLocked,
                         baseLabel, ((IContentType) item
                                 .getData(DATA_FROM_CONTENT_TYPE)).getName()));
     }
     
     /**
      * Place the existing resource types in the table
      */
     protected void fillResourceTypeTable() {
         // Populate the table with the items
 IFileEditorMapping[] array = WorkbenchPlugin.getDefault()
                 .getEditorRegistry().getFileEditorMappings();
         for (int i = 0; i < array.length; i++) {
             FileEditorMapping mapping = (FileEditorMapping) array[i];
             mapping = (FileEditorMapping) mapping.clone(); // want a copy
 newResourceTableItem(mapping, i, false);
         }
     }

     /**
      * Returns the image associated with the given editor.
      */
     protected Image getImage(IEditorDescriptor editor) {
         Image image = (Image) editorsToImages.get(editor);
         if (image == null) {
             image = editor.getImageDescriptor().createImage();
             editorsToImages.put(editor, image);
         }
         return image;
     }

     protected FileEditorMapping getSelectedResourceType() {
         TableItem[] items = resourceTypeTable.getSelection();
         if (items.length > 0) {
             return (FileEditorMapping) items[0].getData(); //Table is single select
 }
         return null;
     }

     protected IEditorDescriptor[] getAssociatedEditors() {
         if (getSelectedResourceType() == null) {
             return null;
         }
         if (editorTable.getItemCount() > 0) {
             ArrayList editorList = new ArrayList ();
             for (int i = 0; i < editorTable.getItemCount(); i++) {
                 editorList.add(editorTable.getItem(i).getData(DATA_EDITOR));
             }

             return (IEditorDescriptor[]) editorList
                     .toArray(new IEditorDescriptor[editorList.size()]);
         }
         return null;
     }

     public void handleEvent(Event event) {
         if (event.widget == addResourceTypeButton) {
             promptForResourceType();
         } else if (event.widget == removeResourceTypeButton) {
             removeSelectedResourceType();
         } else if (event.widget == addEditorButton) {
             promptForEditor();
         } else if (event.widget == removeEditorButton) {
             removeSelectedEditor();
         } else if (event.widget == defaultEditorButton) {
             setSelectedEditorAsDefault();
         } else if (event.widget == resourceTypeTable) {
             fillEditorTable();
         }

         updateEnabledState();

     }

     /**
      * @see IWorkbenchPreferencePage
      */
     public void init(IWorkbench aWorkbench) {
         this.workbench = aWorkbench;
         noDefaultAndApplyButton();
     }

     /*
      * Create a new <code>TableItem</code> to represent the resource
      * type editor description supplied.
      */
     protected TableItem newResourceTableItem(IFileEditorMapping mapping,
             int index, boolean selected) {
         Image image = mapping.getImageDescriptor().createImage(false);
         if (image != null) {
             imagesToDispose.add(image);
         }

         TableItem item = new TableItem(resourceTypeTable, SWT.NULL, index);
         if (image != null) {
             item.setImage(image);
         }
         item.setText(mapping.getLabel());
         item.setData(mapping);
         if (selected) {
             resourceTypeTable.setSelection(index);
         }

         return item;
     }

     /**
      * This is a hook for sublcasses to do special things when the ok
      * button is pressed.
      * For example reimplement this method if you want to save the
      * page's data into the preference bundle.
      */
     public boolean performOk() {
         TableItem[] items = resourceTypeTable.getItems();
         FileEditorMapping[] resourceTypes = new FileEditorMapping[items.length];
         for (int i = 0; i < items.length; i++) {
             resourceTypes[i] = (FileEditorMapping) (items[i].getData());
         }
         EditorRegistry registry = (EditorRegistry) WorkbenchPlugin.getDefault()
                 .getEditorRegistry(); // cast to allow save to be called
 registry.setFileEditorMappings(resourceTypes);
         registry.saveAssociations();
         
         PrefUtil.savePrefs();
         return true;
     }

     /**
      * Prompt for editor.
      */
     public void promptForEditor() {
         EditorSelectionDialog dialog = new EditorSelectionDialog(getControl()
                 .getShell());
         dialog.setEditorsToFilter(getAssociatedEditors());
         dialog
                 .setMessage(NLS.bind(WorkbenchMessages.Choose_the_editor_for_file,getSelectedResourceType().getLabel() ));
         if (dialog.open() == Window.OK) {
             EditorDescriptor editor = (EditorDescriptor) dialog
                     .getSelectedEditor();
             if (editor != null) {
                 int i = editorTable.getItemCount();
                 boolean isEmpty = i < 1;
                 TableItem item = new TableItem(editorTable, SWT.NULL, i);
                 item.setData(DATA_EDITOR, editor);
                 if (isEmpty) {
                     item
                             .setText(editor.getLabel()
                                     + " " + WorkbenchMessages.FileEditorPreference_defaultLabel); //$NON-NLS-1$
 } else {
                     item.setText(editor.getLabel());
                 }
                 item.setImage(getImage(editor));
                 editorTable.setSelection(i);
                 editorTable.setFocus();
                 getSelectedResourceType().addEditor(editor);
                 if (isEmpty) {
                     getSelectedResourceType().setDefaultEditor(editor);
                 }
                 updateSelectedResourceType(); //in case of new default
 }
         }
     }

     /**
      * Prompt for resource type.
      */
     public void promptForResourceType() {
         FileExtensionDialog dialog = new FileExtensionDialog(getControl()
                 .getShell());
         if (dialog.open() == Window.OK) {
             String name = dialog.getName();
             String extension = dialog.getExtension();
             addResourceType(name, extension);
         }
     }

     /**
      * Remove the editor from the table
      */
     public void removeSelectedEditor() {
         TableItem[] items = editorTable.getSelection();
         boolean defaultEditor = editorTable.getSelectionIndex() == 0;
         if (items.length > 0) {
             getSelectedResourceType().removeEditor(
                     (EditorDescriptor) items[0].getData(DATA_EDITOR));
             items[0].dispose(); //Table is single selection
 }
         if (defaultEditor && editorTable.getItemCount() > 0) {
             TableItem item = editorTable.getItem(0);
             // explicitly set the new editor first editor to default
 getSelectedResourceType().setDefaultEditor(
                     (EditorDescriptor) item.getData(DATA_EDITOR));
             if (item != null) {
                 item
                         .setText(((EditorDescriptor) (item.getData(DATA_EDITOR)))
                                 .getLabel()
                                 + " " + WorkbenchMessages.FileEditorPreference_defaultLabel); //$NON-NLS-1$
 }
             if (!isEditorRemovable(item)) {
                 setLockedItemText(item, item.getText());
             }
         }

     }

     /**
      * Remove the type from the table
      */
     public void removeSelectedResourceType() {
         TableItem[] items = resourceTypeTable.getSelection();
         if (items.length > 0) {
             items[0].dispose(); //Table is single selection
 }
         //Clear out the editors too
 editorTable.removeAll();
     }

     /**
      * Add the selected editor to the default list.
      */
     public void setSelectedEditorAsDefault() {
         TableItem[] items = editorTable.getSelection();
         if (items.length > 0) {
             // First change the label of the old default
 TableItem oldDefaultItem = editorTable.getItem(0);
             oldDefaultItem
                     .setText(((EditorDescriptor) oldDefaultItem.getData(DATA_EDITOR))
                             .getLabel());
             // update the label to reflect the locked state
 if (!isEditorRemovable(oldDefaultItem)) {
                 setLockedItemText(oldDefaultItem, oldDefaultItem.getText());
             }
             // Now set the new default
 EditorDescriptor editor = (EditorDescriptor) items[0].getData(DATA_EDITOR);
             getSelectedResourceType().setDefaultEditor(editor);
             IContentType fromContentType = (IContentType) items[0].getData(DATA_FROM_CONTENT_TYPE);
             items[0].dispose(); //Table is single selection
 TableItem item = new TableItem(editorTable, SWT.NULL, 0);
             item.setData(DATA_EDITOR, editor);
             if (fromContentType != null) {
                 item.setData(DATA_FROM_CONTENT_TYPE, fromContentType);
             }
             item
                     .setText(editor.getLabel()
                             + " " + WorkbenchMessages.FileEditorPreference_defaultLabel); //$NON-NLS-1$
 item.setImage(getImage(editor));
             if (!isEditorRemovable(item)) {
                 setLockedItemText(item, item.getText());
             }
             editorTable.setSelection(new TableItem[] { item });
         }
     }

     /**
      * Update the enabled state.
      */
     public void updateEnabledState() {
         //Update enabled state
 boolean resourceTypeSelected = resourceTypeTable.getSelectionIndex() != -1;
         boolean editorSelected = editorTable.getSelectionIndex() != -1;

         removeResourceTypeButton.setEnabled(resourceTypeSelected);
         editorLabel.setEnabled(resourceTypeSelected);
         addEditorButton.setEnabled(resourceTypeSelected);
         removeEditorButton.setEnabled(editorSelected && isEditorRemovable());
         defaultEditorButton.setEnabled(editorSelected);
     }
     
     /**
      * Return whether the selected editor is removable. An editor is removable
      * if it is not submitted via a content-type binding.
      *
      * @return whether the selected editor is removable
      * @since 3.1
      */
     private boolean isEditorRemovable() {
         TableItem[] items = editorTable.getSelection();
         if (items.length > 0) {
             return isEditorRemovable(items[0]);
         }
         return false;
     }
     
     /**
      * Return whether the given editor is removable. An editor is removable
      * if it is not submitted via a content-type binding.
      *
      * @param item the item to test
      * @return whether the selected editor is removable
      * @since 3.1
      */
     private boolean isEditorRemovable(TableItem item) {
         IContentType fromContentType = (IContentType) item.getData(DATA_FROM_CONTENT_TYPE);
         return fromContentType == null;
     }

     /**
      * Update the selected type.
      */
     public void updateSelectedResourceType() {
         // TableItem item = resourceTypeTable.getSelection()[0]; //Single select
 // Image image = ((IFileEditorMapping)item.getData()).getImageDescriptor().getImage();
 // imagesToDispose.addElement(image);
 // item.setImage(image);
 }
 }

